package cn.jofei.server.http.filter;

import cn.jofei.common.ano.Component;
import cn.jofei.common.ano.Order;
import cn.jofei.common.netty.protocol.MessageCodec;
import cn.jofei.common.netty.protocol.ProtocolFrameDecoder;
import cn.jofei.common.util.Utils;
import cn.jofei.netty.hadler.ClientMessageHandler;
import cn.jofei.netty.hadler.HttpServerHandler;
import cn.jofei.netty.hadler.WebSocketHandShakeHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.Promise;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

/**
 * WebSocket 转发过滤器
 *
 * @author Jofei Bao
 * @version ngbao v1.0.2
 * @date 2023年01月16日 23:06
 */
@Slf4j
@Order(index = -199)
@Component
public class WebSocketFilter implements HttpRequestFilter {

    private static final Map<String, WebSocketVersion> VERSION_MAP = new HashMap<>();

    static {
        VERSION_MAP.put("0", WebSocketVersion.V00);
        VERSION_MAP.put("7", WebSocketVersion.V07);
        VERSION_MAP.put("8", WebSocketVersion.V08);
        VERSION_MAP.put("13", WebSocketVersion.V13);
    }

    @Override
    public boolean doFilter(HttpRequest request, ChannelHandlerContext ctx) throws Exception {
        if (isWsRequest(request)) {
            String host = getHost(request);

            if (Utils.isNull(host)) {
                return true;
            }
            // 公网端握手
            String location = wsLocation(request);
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(location, null, false);
            WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(request);
            if (handshaker == null) {
                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
            } else {
                handshaker.handshake(ctx.channel(), request).addListener(c -> {
                    if (c.isSuccess()) {
                        Promise<Channel> channelPromise = HttpRequestFilter.getChannelPromise(ctx, getSubdomain(host));
                        if (Utils.isNull(channelPromise)) {
                            log.error("未找到注册的客户端");
                            return;
                        }
                        channelPromise.addListener(l -> {
                            if (!l.isSuccess()) {
                                log.error("获取链接失败", l.cause());
                                return;
                            }
                            Channel cChannel = (Channel) l.get();

                            cChannel.pipeline().remove(LoggingHandler.class);
                            cChannel.pipeline().remove(ProtocolFrameDecoder.class);
                            cChannel.pipeline().remove(IdleStateHandler.class);
                            cChannel.pipeline().remove(MessageCodec.class);
                            cChannel.pipeline().remove(ClientMessageHandler.class);

                            cChannel.pipeline().addLast(new HttpClientCodec());
                            cChannel.pipeline().addLast(new HttpObjectAggregator(5 * 1024 * 1024));

                            ctx.pipeline().remove(LoggingHandler.class);
                            ctx.pipeline().remove(HttpServerHandler.class);

                            /**
                             * 内网端握手
                             * {@link WebSocketClientHandshaker13#newHandshakeRequest() }
                             */
                            // 移除旧的握手信息
                            HttpHeaders headers = request.headers();
                            WebSocketVersion version = getVersion(headers);
                            headers
                                    .remove(HttpHeaderNames.SEC_WEBSOCKET_KEY)
                                    .remove(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS)
                                    .remove(HttpHeaderNames.SEC_WEBSOCKET_VERSION)
                                    .remove(HttpHeaderNames.CONTENT_LENGTH)
                                    .remove(HttpHeaderNames.CONNECTION)
                            ;
                            URI uri = new URI(location);
                            WebSocketClientHandshaker clientShaker = WebSocketClientHandshakerFactory.newHandshaker(uri, version, null, true, headers);
                            cChannel.pipeline().addLast(new WebSocketHandShakeHandler(clientShaker, cChannel, ctx.channel()));
                            clientShaker.handshake(cChannel).addListener((ChannelFuture channelFuture) -> {
                                if (!channelFuture.isSuccess()) {
                                    log.error("客户端握手失败", channelFuture.cause());
                                }
                            });
                        });
                    } else {
                        log.error("公网握手失败", c.cause());
                    }
                });
            }
            return true;
        }
        return false;
    }

    private String wsLocation(HttpRequest request) {
        String location = request.headers().get(HttpHeaderNames.HOST) + request.uri();
        return "ws://" + location;
    }

    /**
     * 通过请求头信息判断是否为WS请求
     *
     * @param request
     * @return
     */
    private boolean isWsRequest(HttpRequest request) {
        HttpHeaders headers = request.headers();
        return HttpMethod.GET.equals(request.method()) && headers.contains(HttpHeaderNames.SEC_WEBSOCKET_VERSION) && headers.contains(HttpHeaderNames.SEC_WEBSOCKET_KEY);
    }

    /**
     * 获取版本
     *
     * @param headers
     * @return
     */
    private WebSocketVersion getVersion(HttpHeaders headers) {
        String s = headers.get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);
        return VERSION_MAP.get(s);
    }
}
